//
//  RTLBatchRequest.m
//  RTLNetWorking
//
//  Created by 林波 on 16/7/11.
//  Copyright © 2016年 linbo1. All rights reserved.
//

#import "RTLBatchRequest.h"

#ifndef    weakify
#define weakify(x) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
autoreleasepool{} __weak typeof(x) __weak_##x##__ = x; \
_Pragma("clang diagnostic pop")
#endif


#ifndef    strongify
#define strongify(x) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
try{} @finally{} __strong typeof(x) x = __weak_##x##__; \
_Pragma("clang diagnostic pop")

#endif



@interface RTLBatchRequest () <RTLRequestAccessory>

// 所有请求
@property (nonatomic, strong, readwrite) NSArray *requests;

// 存储请求配置
@property (nonatomic, strong, readwrite) NSPointerArray *accessories;

// 请求任务是否在执行
@property (nonatomic, assign, getter=isRunning) BOOL running;

// 请求是否可以被取消
@property (nonatomic, assign, getter=isCanceling) BOOL canceling;
@end

@implementation RTLBatchRequest



#pragma mark - init

- (instancetype)initWithRequests:(NSArray<RTLBaseRequest *> *)requests {
    self = [super init];
    if (self) {
        self.completionQueue = dispatch_get_main_queue();
        self.cancelWhenErrorOccur = YES;
        NSMutableArray *array = [NSMutableArray array];
        for (RTLBaseRequest *request in requests) {
            if (![array containsObject:request]) {
                [request addAccessory:self];
                [array addObject:request];
            } else {
                NSLog(@"Warning, same requests (%@) in batch requests, later removed", [request class]); // 红色
            }
        }
        self.requests = array;
    }
    return self;
}


#pragma mark - Envet


/**
 *  开始任务
 */
- (void)start {
    if (self.isRunning) return;
    self.running = YES;
    [self toggleAccessoriesRequestWillStart];
    [[RTLBatchRequestManager sharedManager] addBatchRequest:self];

    
    NSMutableArray *responseObjectArray = [NSMutableArray  new];
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    __block RTLBaseRequest *lastRequest = nil;
    __block NSError *lastError = nil;
    __block BOOL flag = YES;
    [self toggleAccessoriesRequestDidStart];
    for (RTLBaseRequest *request in self.requests) {
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            @weakify(self)
            [request setCancelCallback:^(__kindof RTLBaseRequest *request) {
                dispatch_group_leave(group);
            }];
            
            [request startRequestSuccessCallback:^(__kindof RTLBaseRequest *request, id responseObject) {
                dispatch_group_leave(group);
                [responseObjectArray addObject:responseObject];
            } failureCallback:^(__kindof RTLBaseRequest *request, NSError *error) {
                @strongify(self)
                lastRequest = request;
                lastError = error;
                if (self.cancelWhenErrorOccur && flag) {
                    flag = NO;
                    [self cancel];
                }
                dispatch_group_leave(group);
            }];
        });
    }
    
    dispatch_group_notify(group, self.completionQueue?self.completionQueue:dispatch_get_main_queue(), ^{
        
        if (self.canceling) {
            self.running = NO;
            !self.cancelCallback?:self.cancelCallback(self);
            self.canceling = NO;
        } else {
            self.running = NO;
            self.canceling = NO;
            
            [self toggleAccessoriesRequestWillStop];
            if (lastError) {
                !self.failureCallback?:self.failureCallback(self, lastRequest, lastError);
            } else {
                !self.successCallback?:self.successCallback(self,responseObjectArray);
            }
            [self toggleAccessoriesRequestDidStop];
        }
        
        lastError = nil;
        lastRequest = nil;
        
        [[RTLBatchRequestManager sharedManager] removeBatchRequest:self];
    });
}

/**
 *  加入任务
 *
 *  @param accessory <#accessory description#>
 */
- (void)addAccessory:(id<RTLRequestAccessory>)accessory {
    [self.accessories addPointer:(__bridge void *)accessory];
}

/**
 *  开始请求
 *
 *  @param success 设置成功回调
 *  @param failure 设置失败回调
 */
- (void)startBatchReqeustSuccessCallback:(RTLBatchRequestSuccessCallback)success failureCallback:(RTLBatchRequestFailureCallback)failure {
    self.successCallback = success;
    self.failureCallback = failure;
    [self start];
}

/**
 *  设置成功和失败回调
 *
 *  @param success 设置成功回调
 *  @param failure 设置失败回调
 */
- (void)setSuccesssCallback:(RTLBatchRequestSuccessCallback)success failureCallback:(RTLBatchRequestFailureCallback)failure {
    self.successCallback = success;
    self.failureCallback = failure;
}

/**
 *  取消请求
 */
- (void)cancel {
    if (self.isCanceling) return;
    self.canceling = YES;
    
    [self cancelAllSubRequest];
}

/**
 *  取消请求
 *
 *  @param cancel 取消请求的回调
 */
- (void)cancelWithCallback:(RTLBatchRequestCancelCallback)cancel {
    self.cancelCallback = cancel;
    [self cancel];
}



/**
 *  开始请求
 *
 *  @param request 请求接口
 */
- (void)requestDidStart:(id)request {
    if (self.canceling && [request isKindOfClass:[RTLBaseRequest class]]) {
        [self cancelAllSubRequest];
    }
}




#pragma mark - Private

/**
 *  取消所有的请求
 */
- (void)cancelAllSubRequest {
    for (RTLBaseRequest *request in self.requests) {
        if (request.canCancel) [request cancel];
    }
}

/**
 *  任务将要开始
 */
- (void)toggleAccessoriesRequestWillStart {
    for (id<RTLRequestAccessory> obj in self.accessories) {
        if ([obj respondsToSelector:@selector(requestWillStart:)]) {
            [obj requestWillStart:self];
        }
    }
}

/**
 *  任务开始
 */
- (void)toggleAccessoriesRequestDidStart {
    for (id<RTLRequestAccessory> obj in self.accessories) {
        if ([obj respondsToSelector:@selector(requestDidStart:)]) {
            [obj requestDidStart:self];
        }
    }
}

/**
 *  任务将要停止
 */
- (void)toggleAccessoriesRequestWillStop {
    for (id<RTLRequestAccessory> obj in self.accessories) {
        if ([obj respondsToSelector:@selector(requestWillStop:)]) {
            [obj requestWillStop:self];
        }
    }
}

/**
 *  任务停止
 */
- (void)toggleAccessoriesRequestDidStop {
    for (id<RTLRequestAccessory> obj in self.accessories) {
        if ([obj respondsToSelector:@selector(requestDidStop:)]) {
            [obj requestDidStop:self];
        }
    }
}

#pragma mark - get

/**
 *  初始化数组
 *
 *  @return <#return value description#>
 */
- (NSPointerArray *)accessories {
    if (!_accessories) {
        _accessories = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsWeakMemory];
    }
    return _accessories;
}

@end


#pragma mark -  RTLBatchRequestManager

@interface RTLBatchRequestManager ()

@property (nonatomic, strong) NSMutableArray *batchRequests;

@end

@implementation RTLBatchRequestManager


/**
 *  单例
 *
 *  @return <#return value description#>
 */
+ (instancetype)sharedManager {
    static RTLBatchRequestManager *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [RTLBatchRequestManager new];
        instance.batchRequests = [NSMutableArray array];
    });
    return instance;
}


/**
 *  批量请求加入数组中
 *
 *  @param batchRequest 批量请求的接口
 */
- (void)addBatchRequest:(RTLBatchRequest *)batchRequest {
    @synchronized (self) {
        [self.batchRequests addObject:batchRequest];
    }
}


/**
 *  移除批量请求
 *
 *  @param batchRequest 批量请求的接口
 */
- (void)removeBatchRequest:(RTLBatchRequest *)batchRequest {
    @synchronized (self) {
        [self.batchRequests removeObject:batchRequest];
    }
}

@end
